#include "EpilIfStatementInterpreter.h"
#include "EpilInterpreterException.h"
#include "EpilInterpreter.h"
#include "EpilExpressionInterpreter.h"
#include "EpilChunkInterpreter.h"
#include "EpilObjectHelper.h"


EpilIfStatementInterpreter::EpilIfStatementInterpreter( EpilInterpreter * interpreter )
: EpilInterpreterBase( interpreter )
, m_exitReason( ECER_Normal )
, m_exitLocation( EpilLexLocation::InvalidLocation )
{
}


void EpilIfStatementInterpreter::Run( const EpilIfStatementCode * code )
{
	const EpilExpressionCodeBase * ifCondition = code->GetIfBlock()->GetCondition();
	EpilExpressionInterpreter conditionInterpreter( this->GetInterpreter() );
	conditionInterpreter.Run( ifCondition );
	EpilObject * conditionObj = conditionInterpreter.GetObject();

	if( EpilObjectHelper::IsConditionTrue( conditionObj ) )
	{
		EpilChunkInterpreter ci( this->GetInterpreter() );
		this->GetInterpreter()->EnterScope();
		try
		{
			ci.Run( code->GetIfBlock()->GetChunk() );
			m_exitReason = ci.GetExitReason();
			m_exitLocation = ci.GetExitLocation();
		}
		catch( ... )
		{
			this->GetInterpreter()->LeaveScope();
			conditionObj->ReleaseCopy();
			throw;
		}
		this->GetInterpreter()->LeaveScope();
		conditionObj->ReleaseCopy();
	}
	else
	{
		conditionObj->ReleaseCopy();

		bool needToEnterElse = true;

		if( code->GetElseIfBlocks().empty() == false )
		{
			for( EpilThenBlockCodeList::const_iterator it = code->GetElseIfBlocks().begin(); it != code->GetElseIfBlocks().end(); ++it )
			{
				const EpilThenBlockCode * curElseifBlock = *it;

				EpilExpressionInterpreter ei( this->GetInterpreter() );
				ei.Run( curElseifBlock->GetCondition() );
				bool result = EpilObjectHelper::IsConditionTrue( ei.GetObject() );
				ei.GetObject()->ReleaseCopy();

				if( result )
				{
					EpilChunkInterpreter ci( this->GetInterpreter() );
					this->GetInterpreter()->EnterScope();
					try
					{
						ci.Run( curElseifBlock->GetChunk() );
					}
					catch( ... )
					{
						this->GetInterpreter()->LeaveScope();
						throw;
					}
					this->GetInterpreter()->LeaveScope();
					m_exitReason = ci.GetExitReason();
					m_exitLocation = ci.GetExitLocation();
					needToEnterElse = false;
					break;
				}
			}
		}

		if( needToEnterElse && code->GetElseBlock() != NULL )
		{
			EpilChunkInterpreter ci( this->GetInterpreter() );
			this->GetInterpreter()->EnterScope();
			try
			{
				ci.Run( code->GetElseBlock() );
			}
			catch( ... )
			{
				this->GetInterpreter()->LeaveScope();
				throw;
			}
			this->GetInterpreter()->LeaveScope();
			m_exitReason = ci.GetExitReason();
			m_exitLocation = ci.GetExitLocation();
		}
	}
}